/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#define OMEN_SOURCE 1

#include <OmenMath.h>

using namespace Omen;

#include <iostream>

///
/// <summary></summary>
///
Vector3d::Vector3d() :
    x(.0),y(.0),z(.0)
{
}

///
/// <summary></summary>
///
Vector3d::Vector3d( const Omen::Float& _x, const Omen::Float& _y, const Omen::Float& _z ) :
    x(_x),y(_y),z(_z)
{
}

float* Vector3d::operator&() 
{
    return &x; // The x, y and z members are next to each other
}

///
/// <summary></summary>
///
Vector3d::Vector3d( const Vector3d& other )
{
    memcpy( &x, &other.x, 3*sizeof(Omen::Float) );
}

///
/// <summary></summary>
///
Vector3d::~Vector3d()
{
    memset( &x, 0, 3*sizeof(Omen::Float));
}

///
/// <summary></summary>
///
Omen::Float Vector3d::length() const
{
    return sqrt( x*x+y*y+z*z );
}

///
/// <summary>Returns the squared length of this vector</summary>
///
Omen::Float Vector3d::squaredLength() const
{
    return x*x+y*y+z*z;
}

///
/// <summary></summary>
///
void Vector3d::normalize() 
{
    Omen::Float len = length();
    x /= len;
    y /= len;
    z /= len;
}

///
/// <summary></summary>
///
Vector3d Vector3d::normalized() const
{
    Vector3d vec(*this);

    Omen::Float len = length();
    vec.x /= len;
    vec.y /= len;
    vec.z /= len;

    if( vec.x > 1.0 || vec.y > 1.0 || vec.z > 1.0 )
    {
        std::cout << "Error in vector3d normalize function!" << std::endl;
    }

    return vec;
}

///
/// <summary></summary>
///
void Vector3d::setCoordinates( const Omen::Float& _x, const Omen::Float& _y, const Omen::Float& _z )
{
    x = _x;
    y = _y;
    z = _z;
}

///
/// <summary></summary>
///
const Omen::Float& Vector3d::operator[]( unsigned char col ) const
{
    col %= 3;
    switch( col )
    {
    case 0 : return x; break;
    case 1: return y;break;
    case 2 : return z;break;
    default: return x; break;
    }
}

///
/// <summary></summary>
///
Omen::Float& Vector3d::operator[]( unsigned char col )
{
    col %= 3;

    switch( col )
    {
    case 0 : return x; break;
    case 1:  return y;break;
    case 2 : return z;break;
    default: return x; break;
    }
}

///
/// <summary></summary>
///
Vector3d& Vector3d::operator*=( Omen::Float scalar )
{
    x *= scalar;
    y *= scalar;
    z *= scalar;

    return *this;
}

///
/// <summary></summary>
///
Vector3d& Vector3d::operator*=( const Vector3d& other )
{
    x *= other.x;
    y *= other.y;
    z *= other.z;
    
    return *this;
}

///
/// <summary></summary>
///
Vector3d& Vector3d::operator+=( const Vector3d& other )
{
    x += other.x;
    y += other.y;
    z += other.z;

    return *this;
}

///
/// <summary></summary>
///
Vector3d& Vector3d::operator-=( const Vector3d& other )
{
    x -= other.x;
    y -= other.y;
    z -= other.z;


    return *this;
}

///
/// <summary></summary>
///
Vector3d& Vector3d::operator/=( Omen::Float scalar )
{
    if( scalar != 0.0 )
    {
        x /= scalar;
        y /= scalar;
        z /= scalar;
    }
    else
    {
        *this = Vector3d();
    }
    return *this;
}

///
/// <summary></summary>
///
Vector3d Vector3d::operator*( Omen::Float scalar ) const
{
    return Vector3d( x*scalar, y*scalar, z*scalar );
}

OMEN_API const Omen::Vector3d Omen::operator*( Omen::Float scalar, const Omen::Vector3d& vector )
{
    return vector * scalar;
}

///
/// <summary></summary>
///
Vector3d Vector3d::operator*( const Vector3d& other ) const
{
    return Vector3d( x*other.x, y*other.y, z*other.z );
}

///
/// <summary></summary>
///
Vector3d Vector3d::operator+( const Vector3d& other ) const
{
    return Vector3d( x+other.x, y+other.y, z+other.z );
}

const Omen::Vector3d operator+( const Omen::Vector3d& v1,const Omen::Vector3d& v2  )
{
    Omen::Vector3d v = v1;
    v += v2;
    return v;
}

///
/// <summary></summary>
///
Vector3d Vector3d::operator-( const Vector3d& other ) const
{
    return Vector3d( x-other.x, y-other.y, z-other.z );
}

///
/// <summary></summary>
///
Vector3d Vector3d::operator/( Omen::Float scalar ) const
{
    if( scalar != 0.0 )
    {
        return Vector3d( x/scalar, y/scalar, z/scalar );
    }
    else
    {
        return Vector3d();
    }
}

Vector3d& Vector3d::operator =( const Omen::Vector3d& other )
{
    x  = other.x;
    y  = other.y;
    z  = other.z;

    return *this;
}

/// <summary>Returns cross product of given two vectors. i.e. a vector that is perpendicular to given two vectors</summary>
Vector3d Vector3d::crossProduct ( const Vector3d& v1, const Vector3d& v2 )
{
    Vector3d v(v1);
    v.x = v1.y*v2.z - v2.y*v1.z;
    v.y = v1.z*v2.x - v2.z*v1.x;
    v.z = v1.x*v2.y - v2.x*v1.y;
    return v;
}

/// <summmry>Returns dot product of given two vectors</summary>
Omen::Float Vector3d::dotProduct ( const Vector3d& v1, const Vector3d& v2 )
{
    Vector3d v(v1);
    v *= v2;
    Omen::Float ret = static_cast<Omen::Float>(0.0);
    
    for(unsigned char i = 0; i < 3; ++i)
    {
        ret += v1[i] * v2[i];
    }
    return ret;
}

///
/// <summary>Returns vector v projected on to this vector</summary>
///
///             <u,v>
/// proju(v) = ------- u    , where <u,v> is (u dot v)
///             <u,u>
///
Vector3d Vector3d::project( const Vector3d& u, const Vector3d& v )
{
    return Vector3d::dotProduct(u,v)/(Vector3d::dotProduct(u,u)) * u;
}

/// <summary>Retruns a vector that is perpendicular to two given vectors</summary>
Vector3d Vector3d::orthoNormalize ( const Vector3d& v1, const Vector3d& v2 )
{
    Vector3d u1 = v1;
    Vector3d u2 = v2 - project(u1,v2);
    return u2;
}

/// <summary>Returns a vector that is perpendicular to all of three given vectros</summary>
Vector3d Vector3d::orthoNormalize ( const Vector3d& v1, const Vector3d& v2, const Vector3d& v3 )
{   
    Vector3d u1 = v1;
    Vector3d u2 = v2 - project(u1,v2);
    Vector3d u3 = v3 - project(u1,v3) - project(u2,v3);
    return u3;
}
/*
///
/// <summary></summary>
///
OMEN_API Omen::Vector3d operator*( Omen::Float scalar, const Omen::Vector3d& vector )
{
    return Omen::Vector3d(vector[0]*scalar,vector[1]*scalar,vector[2]*scalar);
}

///
/// <summary></summary>
///
OMEN_API Omen::Vector3d operator* ( const Omen::Vector3d& vector, Omen::Float scalar )
{
    return Omen::Vector3d(vector[0]*scalar,vector[1]*scalar,vector[2]*scalar);
}

///
/// <summary></summary>
///
OMEN_API Omen::Vector3d operator* ( const Omen::Vector3d& v1, const Omen::Vector3d& v2 )
{
    return Omen::Vector3d(v1[0]*v2[0],v1[1]*v2[1],v1[2]*v2[2]);
}

///
/// <summary></summary>
///
OMEN_API Omen::Vector3d operator+ ( const Omen::Vector3d& v1, const Omen::Vector3d& v2 )
{
    return Omen::Vector3d(v1[0]+v2[0],v1[1]+v2[1],v1[2]+v2[2]);
}

///
/// <summary></summary>
///
OMEN_API Omen::Vector3d operator- ( const Omen::Vector3d& vector )
{
    return Omen::Vector3d(-vector[0],-vector[1],-vector[2]);
}

///
/// <summary></summary>
///
OMEN_API Omen::Vector3d  operator/ ( const Omen::Vector3d& vector, Omen::Float scalar )
{
    return Omen::Vector3d(vector[0]/scalar,vector[1]/scalar,vector[2]/scalar);
}

///
/// <summary></summary>
///
OMEN_API bool     operator== ( const Omen::Vector3d& v1, const Omen::Vector3d& v2 )
{
    return (v1[0]==v2[0] && v1[1]==v2[1] && v1[2]==v2[2]);
}

///
/// <summary></summary>
///
OMEN_API bool operator!=( const Omen::Vector3d& v1, const Omen::Vector3d& v2 )
{
    return (v1[0]!=v2[0] || v1[1]!=v2[1] || v1[2]!=v2[2]);
}
*/

/// EOF